<!DOCTYPE html>
<html>
  <head>
    <title>nCov-Province</title>
    <link rel="stylesheet" href="/static/css/nCovnxt.css"> 
    <script src="/static/js/d3.min.js"></script>
    <script src="/static/js/library/moment.min.js"></script>
    <script src="/static/js/color-strategies.js"></script>
  </head>
  <body style="text-align: center">
    <svg width="1650" height="920" id="mainsvg" class="svgs" style="background-color: #333333;"></svg>
    <script>
        // get main SVG and its attributes & setting hyper-parameters; 
        const svg = d3.select('#mainsvg');
        const width = +svg.attr('width');
        const height = +svg.attr('height');
        const margin = {top: 100, right: 120, bottom: 100, left: 120};
        const innerWidth = width - margin.left - margin.right;
        const innerHeight = height - margin.top - margin.bottom;
        //const xValue = (datum) => {return dates.indexOf(datum['日期'])};
        //const yValue = (datum) => {return datum['确诊人数']};
        //const xValue = d => d['确诊人数'];
        //const xValue = d => d['感染率'];
        //const yValue = d => {return Math.log(d['diffuse_smooth'] + 1)};
        //const yValue = d => {return d['diffuse_smooth']};
        //const yValue = d => {return Math.log(d['newinfec']+1)};
        //const rValue = d => {return Math.sqrt(d['确诊人数']) + 6};
        // const xValue = d => {return Math.log(d['现有确诊'] + 1)}; 
        // const yValue = d => {return Math.log(d['新增确诊'] + 1);};
        // const rValue = d => {return Math.sqrt(d['扩散指数'] * 100) * 3 + 5;};
        const xValue = d => {
            return Math.log(d['现有确诊'] + 1);}; 
        const yValue = d => {
            if (d['确诊人数'] === 0) {
                return 0;
            }
            else {
                return (d['治愈人数'] / d['确诊人数']) * 100;
            }
        }
        const rValue = d => {
            return Math.sqrt(d['新增确诊']) * 1.2 + 2;};
        // const rValue = d => {
        //     console.log(d['确诊人数'], Math.log(d['确诊人数'] + 1));
        //     return Math.sqrt(d['确诊人数'] + 1) * 0.5 + 2;};
        // const rValue = d => {return Math.sqrt(Math.sqrt(d['扩散指数'] * 10000));};
        let xScale, yScale;
        let maxX, maxY;
        let dates; 
        let keys;
        const keys_to_color = province_color;
        const offset_x = province_offset_x;
        const offset_y = province_offset_y;
        let aduration = 1000;
        let metapop;
        let keyHint = '省份';

        const xAxisLabel = '现有确诊人数';
        const yAxisLabel = '治愈率（%）';
        // const xAxisLabel = 'Number of confirmed cases';
        // const yAxisLabel = 'Cure rate (%)';

        let province_name_map = {
                "北京": "Beijing",
                "天津": "Tianjin",
                "河北": "Hebei",
                "山西": "Shanxi",
                "内蒙古": "Neimenggu",
                "辽宁": "Liaoning",
                "吉林": "Jilin",
                "黑龙江": "Heilongjiang",
                "上海": "Shanghai",
                "江苏": "Jiangsu",
                "浙江": "Zhejiang",
                "安徽": "Anhui",
                "福建": "Fujian",
                "江西": "Jiangxi",
                "山东": "Shandong",
                "河南": "Henan",
                "湖北": "Hubei",
                "湖南": "Hunan",
                "广东": "Guangdong",
                "广西": "Guangxi",
                "海南": "Hainan",
                "重庆": "Chongqing",
                "四川": "Sichuan",
                "贵州": "Guizhou",
                "云南": "Yunnan",
                "西藏": "Xizang",
                "陕西": "Shaanxi",
                "甘肃": "Gansu",
                "青海": "Qinghai",
                "宁夏": "Ningxia",
                "新疆": "Xinjiang",
                "香港": "Hong Kong",
                "澳门": "Macau",
                "台湾": "Taiwan"
            }

        const renderinit = function(data, seq, K){
            console.log(d3.extent(data, rValue));
            // Linear Scale: Data Space -> Screen Space; 
            xScale = d3.scaleLinear()
            .domain([d3.min(data, xValue), d3.max(data, xValue)+1]) // "extent" is equivalent to [d3.min(data, xValue), d3.max(data, xValue)]; 
            .range([0, innerWidth - 100])
            .nice();
            // Introducing y-Scale; 
            yScale = d3.scaleLinear()
            .domain(d3.extent(data, yValue).reverse()) // remember to use reverse() to make y-axis start from the bottom; 
            .range([0, innerHeight])
            .nice();

            // generate maxX and maxY; 
            maxX = xScale(d3.max(data, xValue));
            maxY = yScale(d3.max(data, yValue));

            // The reason of using group is that nothing is rendered outside svg, so margin of svg is always blank while margin of group is rendered inside svg; 
            const g = svg.append('g')
            .attr('transform', `translate(${margin.left}, ${margin.top})`)
            .attr('id', 'maingroup');

            // Adding axes; 
            const yAxis = d3.axisLeft(yScale)
            .tickSize(100-innerWidth)
            //.tickFormat(d3.format('.2s'))
            .tickPadding(10); // .tickPadding is used to prevend intersection of ticks; 
            const xAxis = d3.axisBottom(xScale)
            .tickFormat(d3.format('.2s'))
            .tickSize(-innerHeight)
            .tickPadding(10);

            let yAxisGroup = g.append('g').call(yAxis)
            .attr('id', 'yaxis');
            yAxisGroup.append('text')
            .attr('transform', `rotate(-90)`)
            .attr('x', -innerHeight / 2)
            .attr('y', -60)
            .attr('fill', 'white')
            .text(yAxisLabel)
            .attr('text-anchor', 'middle') // Make label at the middle of axis. 
            yAxisGroup.selectAll('.domain').remove(); // we can select multiple tags using comma to seperate them and we can use space to signify nesting; 
            
            let xAxisGroup = g.append('g').call(xAxis)
            .attr('transform', `translate(${0}, ${innerHeight})`)
            .attr('id', 'xaxis');
            xAxisGroup.append('text')
            .attr('y', 60)
            .attr('x', innerWidth / 2)
            .attr('fill', 'white')
            .text(xAxisLabel);
            xAxisGroup.selectAll('.domain').remove();
            
            
            var province_name = ["北京", "天津", "河北", "山西", "内蒙古", "辽宁", "吉林", "黑龙江", "上海", "江苏", "浙江", "安徽", "福建", "江西", "山东", "河南", "湖北", "湖南", "广东", "广西", "海南", "重庆", "四川", "贵州", "云南", "西藏", "陕西", "甘肃", "青海", "宁夏", "新疆", "香港", "澳门", "台湾"]
            
            // var province_name = ["湖北", "广东", "河南", "浙江", "湖南", "安徽", "江西", "江苏", "重庆", "山东", "四川", "黑龙江", "北京", "上海", "河北", "福建", "广西", "陕西", "海南", "云南", "贵州", "山西", "天津", "辽宁", "甘肃", "吉林", "宁夏", "新疆", "内蒙古", "香港", "青海", "台湾", "澳门", "西藏"]
            // draw legend
            var legend = svg.selectAll(".legend")
                .data(province_name)
                .enter().append("g")
                .attr("class", "legend")
                .attr("transform", function(d, i) { return "translate(" + (innerWidth + 30) + "," + (i * 20 + 140) + ")"; });
			
            // draw legend colored rectangles
            legend.append("rect")
            .data(province_name) 
            .attr("x", 0)
            .attr("y", 0)
            .attr("width", 30)
            .attr("height", 15)
            .style("fill", function (d,i) {
                return province_color[d]});
			
            // draw legend text
            legend.append("text")
            .data(province_name)
            .attr("class", "legend_text")
            .attr("x", 40)
            .attr("y", 9)
            .attr("dy", ".2em")
            .attr("fill", 'white')
            .style("text-anchor", "start")
            // .text(d => province_name_map[d]);
            .text(d => d);

            //document.getElementById('yaxis').getElementsByClassName('tick')[1].getElementsByTagName('text')[0].textContent = 'shaokui'
            myticks = [0, 2, 10, 20, 50, 150, 400, 1100, 3000, 8100, 22000, 60000, 160000]
            for(let tid = 0; tid < myticks.length; tid++){
                document.getElementById('xaxis').getElementsByClassName('tick')[tid].getElementsByTagName('text')[0].textContent = myticks[tid];
            }

            //document.getElementById('yaxis').getElementsByClassName('tick')[1].getElementsByTagName('text')[0].textContent = 'shaokui'
            // myticks = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
            myticks = [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
            for(let tid = 0; tid < myticks.length; tid++){
                document.getElementById('yaxis').getElementsByClassName('tick')[myticks.length-1-tid].getElementsByTagName('text')[0].textContent = myticks[tid];
            }
        };
        
        const renderupdate = function(seq){
            const g = d3.select('#maingroup');
            console.log(seq);
            time = seq[0]['日期'];
            // split_time  = time.split('/');
            // time = split_time[1] + "月" + split_time[2] + "日";
            g.selectAll('.date_text').remove();
            g.append("text")
            .data(['seq']) 
            .attr('class', 'date_text')
            .attr("x", innerWidth - 120)
            .attr("y", innerHeight / 10 - 20)
            .attr("dy", ".5em")
            .style("text-anchor", "end")
            .attr("fill", "#a6a5a5")
            .text(time);

            circleupdates = g.selectAll('circle').data(seq, d => d[keyHint]);
            circleenter = circleupdates.enter().append('circle')
            .attr('cy', (datum) => { return yScale(yValue(datum)) })
            .attr('cx', (datum) => { return xScale(xValue(datum)) }) // use xSacle to re-scale data space (domain) and return the rescaled population; 
            .attr('r', datum => rValue(datum))
            .attr('stroke-width', 2)
            .attr('stroke', '#cccccc')
            .attr('fill', function(d,i) {return keys_to_color[d[keyHint]]}) // #3fd4c5
            .attr('opacity', .85);
            circleupdates.merge(circleenter).transition().ease(d3.easeLinear).duration(aduration)
            .attr('cy', (datum) => { return yScale(yValue(datum)) })
            .attr('cx', (datum) => { return xScale(xValue(datum)) }) // use xSacle to re-scale data space (domain) and return the rescaled population; 
            .attr('r', datum => rValue(datum));

            textupdates = g.selectAll('.province_text').data(seq);
            textenter = textupdates.enter().append('text')
            .attr("class", "province_text")
            .attr("x", (datum) => { return xScale(xValue(datum)) + offset_x[datum[keyHint]]; })
            .attr("y", (datum) => { return yScale(yValue(datum)) - 23 + offset_y[datum[keyHint]] + rValue(datum); })
            .attr("dy", ".05em")
            .style("text-anchor", "middle")
            .attr("fill", "#333333")
            // .attr('opacity', 0)
            .text(function (d,i) { 
                return d[keyHint];
            });

            textupdates.merge(textenter).transition().ease(d3.easeLinear).duration(aduration)
            .attr('x', (datum) => { return xScale(xValue(datum)) + offset_x[datum[keyHint]]; })
            .attr('y', (datum) => { return yScale(yValue(datum)) - 23 + offset_y[datum[keyHint]] + rValue(datum); });
        };

        d3.csv('./static/data/2019population.csv').then(data => {
            data.forEach(datum => {
                datum['2019年人口'] = +(datum['2019年人口']);
            })
            metapop = data;
        });

        d3.csv('./static/data/province.csv').then(function(data){
            totals = data.filter(datum => {return datum[keyHint] === '总计'});
            data = data.filter(datum => {return datum[keyHint] !== '总计'});            
            // deletelist = ['澳门', '香港', '甘肃', '青海', '吉林', '内蒙古', '新疆', '台湾', '西藏'];
            // for(let di = 0; di < deletelist.length; di++){
            //     data = data.filter(datum => {return datum[keyHint] !== deletelist[di]});
            // }

            data.forEach(datum => {
                // pre-process the data; 
                datum['现有确诊'] = +(datum['现有确诊']);
                datum['新增确诊'] = +(datum['新增确诊']);
                datum['确诊人数'] = +(datum['确诊人数']);
                datum['治愈人数'] = +(datum['治愈人数']);
                datum['死亡人数'] = +(datum['死亡人数']);
                if(datum['扩散指数'] === undefined){
                    let lastmoment = moment(datum['日期'], 'M月D日', 'en', true)
                    let lastdate = lastmoment.date(lastmoment.date()-1).format('M月D日');
                    datum_last_day = data.find(x => {
                        return x[keyHint] === datum[keyHint] && lastdate === x['日期'];
                    }); 
                    //console.log(datum, datum_last_day);
                    if(datum_last_day === undefined || datum_last_day['确诊人数'] === 0){
                        datum['扩散指数'] = 0.0;
                    }else{
                        datum['扩散指数'] = datum['新增确诊'] / datum_last_day['确诊人数'];
                    }
                }
                if(isNaN(datum['扩散指数'])){
                    datum['扩散指数'] = 0.0;
                }else{
                    datum['扩散指数'] = +(datum['扩散指数']);
                }
                // datum['感染率'] = datum['确诊人数'] / (metapop.find(x => x[keyHint] === datum[keyHint])['2019年人口'] / 10000.0);
            });

            // remove duplicated items;
            // data = data.filter(datum => {
            //     var delete_dates = ['2020/1/21', '2020/1/22', '2020/1/23', '2020/1/24'];
            //     //  '2020/1/25', '2020/1/26', '2020/1/27', '2020/1/28', '2020/1/29', '2020/1/30', '2020/1/31'];
            //     var flag = true;
            //     for(var i=0; i<delete_dates.length; ++i) {
            //         if(datum['日期'] == delete_dates[i]){
            //             flag = false;
            //             break;
            //         }
            //     }
            //     return flag;}) // delete first day data; 
            alldates = Array.from(new Set(data.map( datum => datum['日期'])));
            keys = Array.from(new Set(data.map( datum => datum[keyHint])));

            // make sure dates are listed according to real time order; 
            alldates = alldates.sort(function(a,b){
                // turn your strings into dates, and then subtract them
                // to get a value that is either negative, positive, or zero.
                return new Date(b.date) - new Date(a.date);
            });
            dates = alldates;

            // re-arrange the data sequentially; 
            sequential = []; 
            alldates.forEach(datum => {
                sequential.push([]);
            });
            data.forEach(datum => {
                sequential[alldates.indexOf(datum['日期'])].push(datum);
            });
            
            // calculate 'newinfec' (新增确诊数); 
            let t = 0;
            for(; t < sequential.length; t++){
                sequential[t].forEach(datum => {
                    if(t == 0){
                        datum['newinfec'] = 0;
                    }else{
                        datum['newinfec'] = datum['确诊人数'] - sequential[t-1].find(x => x[keyHint] === datum[keyHint])['确诊人数']
                        if(datum['newinfec'] < 0){
                            datum['newinfec'] = 0;
                        }
                    }
                })
            }

            // 'K' for Wen-Yang; 
            //const lastseq = sequential[sequential.length-1];
            //let total_new = 0;
            //let total_num = 0;
            //lastseq.forEach(d => {total_new += d['newinfec']; total_num += d['确诊人数'];});
            //const K = total_new / total_num;
            const total_last = totals.find(d => {return d['日期'] === alldates[alldates.length-1]}); 
            const total_ls = totals.find(d => {return d['日期'] === alldates[alldates.length-2]}); 
            const K = (total_last['确诊人数'] - total_ls['确诊人数']) / total_last['确诊人数'];
            console.log(K);

            // initialize the chart; 
            renderinit(data, sequential[0], K);

            // set the animation interval; 
            let c = 0; 
            intervalId = setInterval(function(){
                if(c >= alldates.length){
                    console.log('time to close this animation');
                    clearInterval(intervalId); 
                }else{
                    // c = alldates.length - 1;
                    renderupdate(sequential[c]);
                    c = c + 1;
                }
            }, aduration); 
        });
    </script>
  </body>
</html>